import socket
import json
from datetime import datetime
import time


def write_server_variable(var_name, var_value):
    """
    向服务端写入指定变量
    :param var_name: 变量名，必须是 "skill_type"、"terminate_status"、"mouse_status" 之一
    :param var_value: 变量值，需符合服务端要求（skill_type:A/S; terminate_status:Y/N; mouse_status:U/D）
    """
    server_ip = '127.0.0.1'
    server_port = 18787
    s = None  # 初始化socket变量

    try:
        # 创建TCP socket并连接服务器
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect((server_ip, server_port))
        print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]}] 已连接服务器 {server_ip}:{server_port}")

        # 构建RUN模式的JSON指令，严格匹配服务端协议
        request_data = {
            "mode": "RUN",
            "variable": var_name,
            "value": var_value
        }
        # 将字典转为JSON字符串并编码
        request_json = json.dumps(request_data)
        s.sendall(request_json.encode('utf-8'))
        print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]}] 已发送写入请求: {request_json}")

        # 接收服务端响应（1024字节缓冲区，与服务端匹配）
        response = s.recv(1024)
        if response:
            try:
                response_str = response.decode('utf-8')  # 使用utf-8编码更通用
                # 解析JSON响应，提升可读性
                response_json = json.loads(response_str)
                print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]}] 服务端响应:")
                print(f"  状态: {response_json.get('status', '未知')}")
                print(f"  消息: {response_json.get('message', '无消息')}")
                print(f"  当前技能类型: {response_json.get('skill_type', '未知')} (A=AOE/S=单体)")
                print(f"  当前终止状态: {response_json.get('terminate_status', '未知')} (Y=已终止/N=运行中)")
                print(f"  当前鼠标状态: {response_json.get('mouse_status', '未知')} (D=按下/U=抬起)")
            except UnicodeDecodeError:
                print(
                    f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]}] 服务端响应解码失败，原始数据: {response}")
            except json.JSONDecodeError:
                print(
                    f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]}] 服务端响应格式错误，无法解析为JSON: {response_str}")
        else:
            print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]}] 未收到服务端响应")

    except ConnectionRefusedError:
        print(
            f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]}] 无法连接到服务器 {server_ip}:{server_port}，请先启动服务端")
    except socket.timeout:
        print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]}] 连接服务器超时")
    except socket.error as e:
        print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]}] 网络错误: {str(e)}")
    except json.JSONDecodeError:
        print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]}] 服务端返回数据格式错误，无法解析为JSON")
    except Exception as e:
        print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]}] 发生错误: {str(e)}")
    finally:
        # 确保socket资源被释放
        if s:
            try:
                s.close()
                print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]}] 已关闭与服务器的连接")
            except Exception as e:
                print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]}] 关闭连接时发生错误: {str(e)}")
        print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]}] 操作完成\n")


# 测试：修改技能类型为AOE（A），可根据需要修改变量名和值
if __name__ == "__main__":
    # 可选修改方案：
    # 1. 修改终止状态为已终止：write_server_variable("terminate_status", "Y")
    # 2. 修改鼠标状态为按下：write_server_variable("mouse_status", "D")
    # write_server_variable("keyboard_events",
    #                       "[{'key':'v','times':'3','interval':'1000','afterInterval':'10000'},{'key':'b','times':'2','interval':'1000'}]")
    time.sleep(6)
    # write_server_variable("keyboard_events",
    #                       "[{'key':'9','times':'1','interval':'1000','afterInterval':'50'},{'key':'b','times':'1','interval':'1000','afterInterval':'50'}]")
    write_server_variable("keyboard_events",
                          "[{'key':'0','times':'1','interval':'1000','afterInterval':'50'},{'key':'v','times':'v','interval':'60','afterInterval':'50'}]")